#include "Logic.h"

// ************************************************************************ //
// Singleton
// ************************************************************************ //
Logic* Logic::_oneLogic = NULL;

Logic* Logic::getInstance() {
	if( _oneLogic == NULL ) {
		_oneLogic = new Logic();
	}

	return _oneLogic;
}

// ************************************************************************ //
// This is the public function for UI to call
// Scroll down to see how the sub-functions are implemented
// All logic is done in SLAP
// ************************************************************************ //
string Logic::executeUserCommand(string userCommand, vector<Task>& tasksVector) {
	Parser::PARSED_COMMAND parsedCommand;
	Parser::CommandType commandType;

	string statusMessage;

	parsedCommand = _parser->parseUserCommand(userCommand);
	commandType = parsedCommand.commandType;

	if( commandType == Parser::INVALID ) {
		statusMessage = INVALID_COMMAND_MESSAGE;
	}
	else if( commandType == Parser::ADD_FLOATING_TASK 
		|| commandType == Parser::ADD_DEADLINE_TASK
		|| commandType == Parser::ADD_TIMED_TASK ) {
		try {
			statusMessage = _logicAdd->execute(parsedCommand, 
														tasksVector,
														_data,
														_writer);
		} catch (string err) {
			statusMessage = err;
			// logError
		}
	} else if( commandType == Parser::DELETE_TASKID 
		|| commandType == Parser::DELETE_DONE
		|| commandType == Parser::DELETE_DONE ) {
		try {
			statusMessage = _logicDelete->execute(parsedCommand, 
														tasksVector,
														_data,
														_writer);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::LIST_ALL 
		|| commandType == Parser::LIST_SEARCH_TERM
		|| commandType == Parser::LIST_DAY
		|| commandType == Parser::LIST_WEEK
		|| commandType == Parser::LIST_MONTH ) {
		try {
			statusMessage = _logicList->execute(parsedCommand, 
														tasksVector,
														_data,
														_writer);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::EDIT_RENAME 
		|| commandType == Parser::EDIT_START_DATETIME
		|| commandType == Parser::EDIT_START_TIME
		|| commandType == Parser::EDIT_END_DATETIME
		|| commandType == Parser::EDIT_END_TIME ) {
		try {
			statusMessage = _logicEdit->execute(parsedCommand, 
														tasksVector,
														_data,
														_writer);
		} catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::SORT_ALPHABET
		|| commandType == Parser::SORT_TIME
		|| commandType == Parser::SORT_COMPLETED
		|| commandType == Parser::SORT_FLAG ) {
		try {
			statusMessage = _logicSort->execute(parsedCommand, 
														tasksVector,
														_data,
														_writer);
		}  catch (string err) {
			statusMessage = err;
		}
	} else if( commandType == Parser::DONE_TASKID 
		|| commandType == Parser::DONE_SEARCHTERM ) {
		try {
			statusMessage = _logicDone->execute(parsedCommand, 
														tasksVector,
														_data,
														_writer);
		} catch (string err) {
			statusMessage = err;
		}
	}

	return statusMessage;
}
// ************************************************************************ //
// ***							END OF API								*** //
// ************************************************************************ //


//*********************************************************//
//***				POINTS TO NOTE ON LOGIC				***//
//*********************************************************//
// 1. tasksVector is updated for UI display
// 2. 10 undo writer objects + 1 main writer
// 3. undo writer objects records 1 less function call
// 4. _undoState indexes current undo writer object
// 5. logErrors called upon internal error
//*********************************************************//
//***					END OF SECTION					***//
//*********************************************************//


// ******************************************//
// FUNCTIONS BELOW ARE PRIVATE SUB-FUNCTIONS //
// ******************************************//

// setup for when user first fires up program
void Logic::setupParser() {
	_parser = new Parser();
}

void Logic::setupData() {
	_data = new Data();
}

void Logic::setupMainWriter() {
	_writer = new Writer(FILE_NAME);
}

// read and store in Data
void Logic::populateData() {
	
	vector<Task> initVector;

	_writer->read(initVector);
	_data->executeLoad(initVector);
}

// *********************************** //
// START OF CONSTRUCTOR AND DESTRUCTOR //
// *********************************** //
Logic::Logic() {

	// sets up the program before user begins using it
	// populates the internal vector with tasks from files
	setupParser();
	setupData();
	setupMainWriter();

	_logicAdd = new LogicAdd();
	_logicDelete = new LogicDelete();
	_logicSort = new LogicSort();
	_logicList = new LogicList();
	_logicEdit = new LogicEdit();
	_logicDone = new LogicDone();
	_logicFlag = new LogicFlag();
	_logicClear = new LogicClear();
	_logicUndo = new LogicUndo();

	// 10 different undo states
	// it will be cycled when a command is successfully invoked
	/*_logicUndo->setupWriters(states);
	_logicUndo->setInitUndoState(INIT_UNDO_STATE);*/
	populateData();
}

Logic::~Logic() {
	fstream log;
	log.open(ERROR_LOG_FILE_NAME, fstream::in | fstream::app);

	log << ERROR_LOG_FILE_EOF << endl;

	log.close();

	delete _parser;
	delete _data;
	delete _writer;

	delete _logicAdd;
	delete _logicDelete;
	delete _logicSort;
	delete _logicList;
	delete _logicEdit;
	delete _logicDone;
	delete _logicFlag;
	delete _logicClear;
	delete _logicUndo;

	/*for( int i=0; i<MAX_UNDO_STATES; i++ ) {
		delete states[i];
	}*/
}
// ********************************* //
// END OF CONSTRUCTOR AND DESTRUCTOR //
// ********************************* //

bool Logic::isOutOfBounds(int index, int size) {
	bool outOfBounds;

	if( index <= 0 ) {
		outOfBounds = true;
	} else if( index > size ) {
		outOfBounds = true;
	} else {
		outOfBounds = false;
	}

	return outOfBounds;
}

// logging error messages
void Logic::logErrors(string errorMessage, int undoState) {
	fstream log;
	log.open(ERROR_LOG_FILE_NAME, fstream::in | fstream::app);

	log << errorMessage << endl;

	log.close();
}

// constants
// status messages
const string Logic::INVALID_COMMAND_MESSAGE = "Invalid Command";

// main file name
const string Logic::FILE_NAME = "schedulerPP.txt";
const string Logic::ERROR_LOG_FILE_EOF = "========END========";

// error log file name
const string Logic::ERROR_LOG_FILE_NAME = "log.txt";